package be.msec.smartcard;


import javacard.framework.Util;
import javacard.security.KeyBuilder;
import javacard.security.RSAPublicKey;
import javacard.security.Signature;


public class Cert {
	
	private byte domain;
	private byte[] issuer;
	public byte[] subject;
	public RSAPublicKey publicKey;
	public byte[] signature;
	public byte[] rawCert;
	public Date endDate;
	public short publicKeyModulusLength;
	public short publicKeyExponentLength;
	
	public byte GetDomain() {
		return this.domain;
	}
	
	private Cert() {

	}
	
	private void setData(RSAPublicKey publicKey, byte domainID, byte[] issuer, byte[] subject, byte[] endDate, byte[] signature, byte[] rawCert) {
		this.publicKey = publicKey;
		this.domain = domainID;
		this.issuer = issuer;
		this.subject = subject;
		this.signature = signature;
		this.rawCert = rawCert;
		this.endDate = new Date(endDate, (byte)0);
	}
	
	public boolean Verify(RSAPublicKey publicKey) {
		//Verify the received signature
		Signature signature = Signature.getInstance (Signature.ALG_RSA_SHA_PKCS1, false) ;
		signature.init(publicKey, Signature.MODE_VERIFY) ;
		boolean verify = signature.verify(
								this.rawCert,				 							// data to be verified
								(short)0, 												// offset
								(short)(this.rawCert.length-this.signature.length-2), 	// Certificate length = rawCert - signature bytes - 2 bytes (=length of signature)
								this.signature, 										// signature bytes
								(short)0,					 							// Offset 
								(short)this.signature.length);							// signature length
		return verify;
	}


	/**
	 * 
	 * @param certificate the byte representation of a certificate ( custom protocol ) 
	 * @return Cert object
	 * @throws InvalidKeySpecException
	 * @throws NoSuchAlgorithmException
	 */
	public static Cert FromByteArray(byte[] certificate) {
		//Domain ID - 1 byte
		//subject - x bytes
		//issuer - y bytes
		//end year - 2 bytes
		//end month - 1 byte
		//end day - 1 byte
		//Public key modulus length - 2 bytes (short)
		//Public key exponent length - 2 bytes (short)
		//Public key modulus
		//Public key exponent
		//Signature length - 2 bytes (short)
		//Signature
		
		short offset = 0;
		byte domainID = certificate[offset];
		offset++;
		Cert cert = new Cert();
		
		
		/***** Read subject *****/
		byte[] subject = null;
		while(certificate[offset] != 0 ) {
			subject = addByte(subject, certificate[offset]);
			offset++;
		}
		subject = addByte(subject, certificate[offset]); //Add the last 0
		offset++;
		
		
		/***** Read issuer *****/
		byte[] issuer = null;
		while(certificate[offset] != 0 ) {
			issuer = addByte(issuer, certificate[offset]);
			offset++;
		}
		issuer = addByte(issuer, certificate[offset]); //Add the last 0
		offset++;
		
		
		/***** Read End Date *****/
		byte[] endDate = new byte[4];
		Util.arrayCopy(certificate, offset, endDate, (short)0, (short)4);	//endDate = 4 bytes (2 bytes year, 1 byte month and 1 byte day)
		offset += 4;
		
		
		/***** Read key length *****/
		cert.publicKeyModulusLength = (short) (((certificate[offset] << 8)) | ((certificate[offset + 1] & 0xff)));
		offset += 2;
		cert.publicKeyExponentLength = (short) (((certificate[offset] << 8)) | ((certificate[offset + 1] & 0xff)));
		offset += 2;
		
		
		/***** Copy key *****/
		byte offsetModulus = 0, offsetExponent = 0;
		if(certificate[offset] == 0 ) offsetModulus = 1;								//Om die eerste 0 van de BigInteger->Array weg te werken!
		if(certificate[offset+cert.publicKeyModulusLength] == 0 ) offsetExponent = 1;		//Om die eerste 0 van de BigInteger->Array weg te werken!
		
		byte[] publicKeyModulus = new byte[cert.publicKeyModulusLength-offsetModulus];
		byte[] publicKeyExponent = new byte[cert.publicKeyExponentLength-offsetExponent];
		Util.arrayCopy(certificate, (short)(offset+offsetModulus), publicKeyModulus, (short)0, (short)(cert.publicKeyModulusLength-offsetModulus));
		offset += cert.publicKeyModulusLength;
		Util.arrayCopy(certificate, (short)(offset+offsetExponent), publicKeyExponent, (short)0, (short)(cert.publicKeyExponentLength-offsetExponent));
		offset += cert.publicKeyExponentLength;
		
		
		/***** Read signature length *****/
		short signatureLength = (short) (((certificate[offset] << 8)) | ((certificate[offset + 1] & 0xff)));
		offset += 2;
		
		
		/***** Read signature *****/
		byte[] signature = new byte[signatureLength];
		Util.arrayCopy(certificate, offset, signature, (short)0, (short)signatureLength);
		
		
		RSAPublicKey myPublicKey = (RSAPublicKey) KeyBuilder.buildKey (KeyBuilder.TYPE_RSA_PUBLIC, (short)512 , false) ;
		myPublicKey.setModulus(publicKeyModulus, (short)0, (short)(publicKeyModulus.length));
		myPublicKey.setExponent(publicKeyExponent, (short)0, (short)(publicKeyExponent.length));
		cert.setData(myPublicKey, domainID, issuer, subject, endDate, signature, certificate);
		return cert;
	}
	
	
	private static byte[] addByte(byte[] array, byte b){
		if(array == null ) {
			byte[] out = new byte[1];
			out[0] = b;
			return out;
		}
		else{
			byte[] out = new byte[array.length+1];
			Util.arrayCopy(array, (short)0, out, (short)0, (short)array.length);
			out[out.length-1] = b;
			return out;
		}
	}
}
